﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using DoNet.Utils;

namespace DoNet.TenPay
{
    public class SignatureV3
    {
        /// <summary>
        /// 获取HTTP头的授权信息
        /// </summary>
        /// <param name="url">微信的接口地址</param>
        /// <param name="method">请求的方式GET,POST,PUT</param>
        /// <param name="jsonData">post请求的数据，json格式  ，get时传空</param>
        /// <param name="merchantId">发起请求的商户（包括直连商户、服务商或渠道商）的商户号 mchid</param>
        /// <param name="certPath">商户API证书保存路径</param>
        /// <returns></returns>
        public static string GetAuthorization(string url, string method, string jsonData, string merchantId)
        {
            //获取请求的绝对URL，并去除域名部分得到参与签名的URL
            var uri = new Uri(url);
            string urlPath = uri.PathAndQuery;
            //随机串
            string nonce = StringUtil.GenerateNonceStr();
            //获取发起请求时的系统当前时间戳
            var timestamp = TimeUtil.GenerateTimeStamp();

            //构造的请求签名串
            if (string.IsNullOrEmpty(jsonData))
            {
                jsonData = "";
            }
            string data = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", method, urlPath, timestamp, nonce, jsonData);
            string signTxt = Sign(data, merchantId);

            //获取API证书的序列号
            string serialNo = "";
            using (X509Certificate2 cert = GetCert(merchantId))
            {
                if (cert != null)
                {
                    serialNo = cert.SerialNumber;
                }
            }

            //Authorization和格式
            return string.Format("WECHATPAY2-SHA256-RSA2048 mchid=\"{0}\",nonce_str=\"{1}\",timestamp=\"{2}\",serial_no=\"{3}\",signature=\"{4}\"", merchantId, nonce, timestamp, serialNo, signTxt);
        }

        /// <summary>
        /// 生成签名
        /// </summary>
        /// <param name="data">构造的请求签名串</param>
        /// <param name="merchantId">发起请求的商户（包括直连商户、服务商或渠道商）的商户号 mchid</param>
        /// <returns></returns>
        public static string Sign(string data, string merchantId)
        {
            string signResult = "";
            //证书
            using (X509Certificate2 cert = GetCert(merchantId))
            {
                if (cert != null)
                {
                    //获取签名证书私钥
                    using (RSA rsa = cert.GetRSAPrivateKey())
                    {
                        using (RSACryptoServiceProvider rsaClear = new RSACryptoServiceProvider())
                        {
                            rsaClear.ImportParameters(rsa.ExportParameters(true));
                            var signData = rsa.SignData(Encoding.UTF8.GetBytes(data), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                            signResult = Convert.ToBase64String(signData);
                        }
                    }
                }
            }
            return signResult;
        }

        /// <summary>
        /// 验证签名
        /// </summary>
        /// <param name="signature">微信支付的应答签名通过HTTP头Wechatpay-Signature传递</param>
        /// <param name="signSourceString">验签名串-应答时间戳\n应答随机串\n应答报文主体\n</param>
        /// <param name="serialNo">微信支付的平台证书序列号位于HTTP头Wechatpay-Serial</param>
        /// <param name="merchantId">发起请求的商户（包括直连商户、服务商或渠道商）的商户号 mchid</param>
        /// <param name="apiKeyV3">商户号API密钥V3</param>
        /// <returns></returns>
        public static bool VerifySign(string signature, string signSourceString, string serialNo, string merchantId, string apiKeyV3)
        {
            bool isVerify = false;

            //平台证书
            var plainCertificate = CertApi.GetCertsResult(merchantId, apiKeyV3, serialNo);
            if (!string.IsNullOrEmpty(plainCertificate))
            {
                X509Certificate2 certPlatform = new X509Certificate2(Encoding.UTF8.GetBytes(plainCertificate));
                if (certPlatform != null)
                {
                    var rsaParam = certPlatform.GetRSAPublicKey().ExportParameters(false);
                    using (var rsa = new RSACryptoServiceProvider())
                    {
                        rsa.ImportParameters(rsaParam);
                        using (var sha256 = new SHA256CryptoServiceProvider())
                        {
                            isVerify = rsa.VerifyData(Encoding.UTF8.GetBytes(signSourceString), sha256, Convert.FromBase64String(signature));
                        }
                    }
                }
            }
            return isVerify;
        }

        /// <summary>
        /// 获取对应API证书
        /// </summary>
        /// <param name="merchantId">发起请求的商户（包括直连商户、服务商或渠道商）的商户号 mchid</param>
        /// <returns></returns>
        public static X509Certificate2 GetCert(string merchantId)
        {
            X509Certificate2 cert = null;
            //证书路径
            string path = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory) + Path.DirectorySeparatorChar + "upload" + Path.DirectorySeparatorChar + "cert" + Path.DirectorySeparatorChar + merchantId + Path.DirectorySeparatorChar;
            string certPath = path + "apiclient_cert.p12";
            string certPwd = merchantId; //API证书的密码一般就是商户号
            //存在证书文件
            if (File.Exists(certPath))
            {
                cert = new X509Certificate2(certPath, certPwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
            }
            return cert;
        }

        /// <summary>
        /// 最终提交请求时，需对敏感信息加密，如身份证、银行卡号。
        /// 加密请求消息中的敏感信息时，使用最新的平台证书（即：证书启用时间较晚的证书）
        /// </summary>
        /// <param name="text">要加密的明文</param>
        /// <param name="merchantId">发起请求的商户（包括直连商户、服务商或渠道商）的商户号 mchid</param>
        /// <param name="apiKeyV3">商户号API密钥V3</param>
        /// <returns></returns>
        public static string RSAEncrypt(string text, string merchantId, string apiKeyV3)
        {
            string result = "";
            //最新的平台证书
            var certModel = CertApi.GetCertificate(merchantId, apiKeyV3);
            if (certModel != null)
            {
                var plainCertificate = certModel.PlainCertificate;
                if (!string.IsNullOrEmpty(plainCertificate))
                {
                    using (X509Certificate2 certPlatform = new X509Certificate2(Encoding.UTF8.GetBytes(plainCertificate)))
                    {
                        if (certPlatform != null)
                        {
                            var rsaParam = certPlatform.GetRSAPublicKey().ExportParameters(false);
                            using (var rsa = new RSACryptoServiceProvider())
                            {
                                rsa.ImportParameters(rsaParam);
                                var buff = rsa.Encrypt(Encoding.UTF8.GetBytes(text), true);
                                result = Convert.ToBase64String(buff);
                            }
                        }
                    }
                }
            }
            return result;
        }
    }
}
